﻿/*
    TeamMatches for Kaixin001.com 

    Copyright (C) 2008, TurboZV, turbozv@gmail.com, http://www.turbozv.com/

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    *KEEP THIS HEADER INFORMATION*
*/

using System;
using System.Collections.ObjectModel;
using System.IO;
using System.IO.Compression;
using Helper;
using System.Web;
using System.Net;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace TeamMatches
{
    public class TeamMatches
    {
        public void Run()
        {
            ReadGlobalConfig();

            if (!File.Exists(c_MasterIDsFile))
            {
                TraceLog.Error("{0}文件不存在！", c_MasterIDsFile);
                return;
            }
            if (!File.Exists(c_SlaveIDsFile))
            {
                TraceLog.Error("{0}文件不存在！", c_SlaveIDsFile);
                return;
            }
            // 读取所有Id
            string[] masterLines = File.ReadAllLines(c_MasterIDsFile);
            string[] slaveLines = File.ReadAllLines(c_SlaveIDsFile);

            TraceLog.PrintLn("开始为好友加油：");

            for (int i = 0; i + 1 < slaveLines.Length; i += 2)
            {
                Logon(slaveLines[i], slaveLines[i + 1]); // 逐个登录从号
                getFriendsList();
                MatchDetail(); //Slave 加油
            }

            for (int i = 0; i + 1 < masterLines.Length; i += 2)
            {
                Logon(masterLines[i], masterLines[i + 1]); // 逐个登录主号
                getFriendsList();
                MatchDetail(); //Master 加油
                TeamBegin(); //Master 启动
                TraceLog.PrintLn("Sleep 1 mins....");
                Thread.Sleep(1000*60*1); //Sleep 1 分钟防止冲突
            }

            TraceLog.PrintLn("");
            TraceLog.PrintLn("按任意键退出....");
            //Console.ReadKey();
        }

        private void getFriendsList()
        {
            TraceLog.Print("获取好友列表......");
            //var v_frienddata = [{"uid":"341547","real_name":"\u90b5\u4f73\u8335","ta":"\u5979","full":"1","scenemoney":"11","scenename":"\u5927\u5267\u9662","neighbor":0,"online":0},{"uid":"511409","real_name":"\u5200\u5200","ta":"\u4ed6","full":"0","scenemoney":"10","scenename":"\u6c34\u4e61","neighbor":0,"online":0}
            this.fList = new Collection<int>();
            string content = _hh.Get("http://www.kaixin001.com/app/app.php?aid=1040");
            string fContent = ContentHelper.GetMidString(content, "var v_frienddata =", "];");
            int pos = 0;
            while (pos >= 0)
            {
                string uid = ContentHelper.GetMidString(content, "\"uid\":\"", "\"", out pos);
                if (pos < 0) break;
                content = content.Substring(pos);
                fList.Add(Int32.Parse(uid));
            }
            TraceLog.PrintLn("OK");
            return;
        }

        private bool TeamCreate(string content)
        {
            //http://www.kaixin001.com/parking/matchorigin.php?
            //verify=525328_1040_525328_1238394118_56b0b38f58f77ea2c9eb2e8713da1b7a&teamid=7416185&lineid=2&maxteamnum=2
            //<a href="javascript:matchOrigin(5944407);" class="sl">发起比赛
            int pos = 0;
            string teamid = ContentHelper.GetMidString(content, "href=\"javascript:matchOrigin(", ")", out pos);
            if ((null == teamid) || (pos < 0)) 
            {
                TraceLog.PrintLn("无法发起比赛");
                return false;
            }

            string verifyCode = ContentHelper.GetVerifyCode(content);
            string link = "http://www.kaixin001.com/parking/matchorigin.php?";
            string param = "verify=" + verifyCode + "&teamid=" + teamid + "&lineid=2&maxteamnum=2";
            TraceLog.PrintLn(link);
            TraceLog.PrintLn(param);
            string contentNew = _hh.Post(link, param);

            return true;
        }

        private bool TeamJoin(string content)
        {
            //http://www.kaixin001.com/parking/matchjoin.php?
            //verify=16007679_1040_16007679_1238394549_083ae33142986a823540928c0f702279&matchuid=525328&matchid=6107800
            //javascript:matchJoin(525328,6107800);

            int pos = 0;
            string tmp = ContentHelper.GetMidString(content, "href=\"javascript:matchJoin(", ")", out pos);
            if (tmp == null)
            {
                TraceLog.PrintLn("无法加入比赛 - search fail");
                return false;
            }
            string[] arr = tmp.Split(',');
            if ((arr.Length != 2) || (pos < 0)) 
            {
                TraceLog.PrintLn("无法加入比赛 - split fail");
                return false;
            }

            string matchuid = arr[0];
            string matchid = arr[1];
            if ((null == matchuid) || (null == matchid) ) 
            {
                TraceLog.PrintLn("无法加入比赛 - invalid param");
                return false;
            }

            string verifyCode = ContentHelper.GetVerifyCode(content);
            string link = "http://www.kaixin001.com/parking/matchjoin.php?";
            string param = "verify="+verifyCode+"&matchuid="+matchuid+"&matchid="+matchid;
            TraceLog.PrintLn(link);
            TraceLog.PrintLn(param);
            string contentNew = _hh.Post(link, param);

            return true;
        }

        private bool getMyselfIDs(out string myMatchUid, out string myMatchId, out string content)
        {
            int pos = 0;
            myMatchUid = null;
            myMatchId = null;
            content = null;

            string link = "http://www.kaixin001.com/app/app.php?aid=1040&url=myteam.php";
            content = _hh.Get(link);
            if (null == content)
            {
                return false;
            }

            //<a href="javascript:matchDetail(
            myMatchUid = ContentHelper.GetMidString(content, "<a href=\"javascript:matchDetail(", ",", out pos);
            if ((null == myMatchUid) || (pos < 0))
            {
                return false;
            }

            content = content.Substring(pos);
            myMatchId = ContentHelper.GetMidString(content, ",", ")", out pos);
            if ((null == myMatchId) || (pos < 0))
            {
                return false;
            }

            content = content.Substring(pos);
            return true;
        }

        private void TeamBegin()
        {
            string myMatchUid, myMatchId, content;
            
            if (! this.getMyselfIDs(out myMatchUid, out myMatchId, out content) ) // 没有车队
            {
                TraceLog.PrintLn("没有自己的车队 或者 没有发起比赛");

                TraceLog.Print("尝试加入比赛....");
                bool operating = this.TeamJoin(content);
                TraceLog.PrintLn("" + operating);

                if (!operating)
                {
                    TraceLog.Print("尝试发起比赛....");
                    operating = this.TeamCreate(content);
                    TraceLog.PrintLn("" + operating);
                }

                //无论成功或者失败都退出，因为第二天才能启动赛车
                return;
            }

            int pos1;
            content = _hh.Get("http://www.kaixin001.com/app/app.php?aid=1040&url=match.php&matchuid=" + myMatchUid + "&matchid=" + myMatchId);

            //记录当前车队的状况
            this.log(content); 

            // 检查车队是否已经启动
            if (content.IndexOf("你的车队今天已经启动") != -1)
            {
                TraceLog.PrintLn("你的车队今天已经启动");
                return;
            }

            // 开始启动比赛
            string verifyCode = ContentHelper.GetVerifyCode(content);
            if (String.IsNullOrEmpty(verifyCode))
            {
                TraceLog.PrintLn("Can not get Verifycode");
                return;
            }
            string teamuid = ContentHelper.GetMidString(content, "javascript:teamBegin(", ",", out pos1);
            if ((null == teamuid)||(pos1 < 0))
            {
                TraceLog.PrintLn("Can not get teamuid");
                return;
            }
            content = content.Substring(pos1);
            string teamid = ContentHelper.GetMidString(content, ",", ")", out pos1);
            if (null == teamid)
            {
                TraceLog.PrintLn("Can not get teamid");
                return;
            }

            _hh.Get("http://www.kaixin001.com/parking/teambegin.php?verify=" + verifyCode + "&teamuid=" + teamuid + "&teamid=" + teamid);

            TraceLog.PrintLn("启动比赛命令发送！");
        }

        private void log(string matchcont)
        {
            //<span class="c9">( 全国拉力赛，总长3506km)</span>
            string temp = ContentHelper.GetMidString(matchcont, "<span class=\"c9\">", "</span>");
            if (String.IsNullOrEmpty(temp)) return;
            string matchName = ContentHelper.GetMidString(temp, "( ", "，");
            string matchLength = ContentHelper.GetMidString(temp, "总长", "km)");

            //<div class="c3 mt5 ml15" ><b>我的车队：</b></div>
            int pos = matchcont.IndexOf("我的车队：");
            if (pos < 0) return;
            temp = matchcont.Substring(pos);
            if (String.IsNullOrEmpty(temp)) return;
            //<div class="cs_s tac" style="margin-left:0;">已经行驶0km<br/>
            temp = ContentHelper.GetMidString(temp, "<div class=\"cs_s tac\" style=\"margin-left:0;\">", "<br/>");
            if (String.IsNullOrEmpty(temp)) return;
            string alreadyFinished = ContentHelper.GetMidString(temp, "已经行驶", "km");

            //<b class="r f14 c9">第 <img src="http://img.kaixin001.com.cn/i2/park/match/0a.gif" align="absmiddle" /> 天</b>
            temp = ContentHelper.GetMidString(matchcont, "<b class=\"r f14 c9\">", "</b>");
            if (String.IsNullOrEmpty(temp)) return;
            string finishedDays = "";
            while (true)
            {
                string daynum = ContentHelper.GetMidString(temp, "match/", "a.gif", out pos);
                if (String.IsNullOrEmpty(daynum) || pos < 0) break;
                temp = temp.Substring(pos);
                finishedDays += daynum;
            }

            int speed = 0;
            int eta = 0;
            int usedDay = -1;
            if (!String.IsNullOrEmpty(finishedDays))
            {
                usedDay = ContentHelper.GetInteger(finishedDays) - 1;
            }
            if (!String.IsNullOrEmpty(alreadyFinished) && !String.IsNullOrEmpty(matchLength))
            {
                int afsh = ContentHelper.GetInteger(alreadyFinished);
                int tlen = ContentHelper.GetInteger(matchLength);
                if (usedDay > 0) speed = afsh / usedDay;
                if (speed > 0) eta = 1 + (tlen - afsh) / speed;
            
            }

            string logs = "" + matchName + "，第" + usedDay + "天，完成：" + alreadyFinished + "km/" + matchLength + "km，" +
                          "速度：" + speed + "km/天，ETA：" + eta + "天。     ";
            TraceLog.PrintLn(logs);

            //Write this parking to log
            TraceLog.Print("Wring to Log: ");

            string content = _hh.Get("http://www.kaixin001.com/app/app.php?aid=1018&url=index.php");
            string verify = ContentHelper.GetMidString(content, "<input type=\"hidden\" name=\"verify\" value=\"", "\">");

            content = @"verify=" + verify + "&word=" + logs + "&privacy=1&noticefriend=1";
            _hh.Post(@"http://www.kaixin001.com/record/submit.php", content);

            TraceLog.PrintLn("Ok!\n");
        }

        private bool getFriendsIDs(string content, out string matchUid, out string matchId, out int pos)
        {
            matchUid = null;
            matchId = null;
            pos = -1;
           

            matchUid = ContentHelper.GetMidString(content, "javascript:matchDetail(", " ,", out pos);
            if (String.IsNullOrEmpty(matchUid) || (pos < 0)) return false;

            content = content.Substring(pos);

            int pos1 = -1;
            matchId = ContentHelper.GetMidString(content, " , ", ")", out pos1);
            if (String.IsNullOrEmpty(matchId) || (pos1 < 0)) return false;

            return true;
        }

        private void MatchDetail()
        {
            string myMatchUid, myMatchId, content;
            this.getMyselfIDs(out myMatchUid, out myMatchId, out content);

            if (null == content) return;
            
            int pos = 0;
            int subpos = 0; 
            int matchCnt = 0;

            string tmp = content;
            while (true)
            {
                string test = ContentHelper.GetMidString(tmp, "javascript:matchDetail(", " ,", out pos);
                if (String.IsNullOrEmpty(test) || (pos < 0)) break;

                tmp = tmp.Substring(pos);
                matchCnt++;
            }
            TraceLog.PrintLn("列表里面共有{0}场比赛！",matchCnt);

            string matchUid = null;
            string matchId = null;
            while (true)
            {
                if (matchCnt >= 6)
                {
                    string subcon = ContentHelper.GetMidString(content, "title=\"我可以去加油\"", "</div>", out pos);
                    if (String.IsNullOrEmpty(subcon) || pos < 0) break;
                    content = content.Substring(pos);

                    if (!this.getFriendsIDs(subcon, out matchUid, out matchId, out subpos)) continue;
                }
                else
                {
                    if (!this.getFriendsIDs(content, out matchUid, out matchId, out pos)) break;
                    content = content.Substring(pos);
                }

                if ((null != myMatchUid) && (myMatchUid.Equals(matchUid)))
                {
                    continue; // 跳过自己参与的比赛
                }

                TeamOil(matchUid, matchId);
            }
        }


        private bool TeamOil(string matchuid, string matchid)
        {
            if ((null == matchuid) || (null == matchid)) return false;

            if (Int32.Parse(matchuid) == this.myID)
            {
                TraceLog.PrintLn("Skip MY Match!");
                return false;
            }
            TraceLog.PrintLn("检视比赛 UID=" + matchuid + ", MID=" + matchid);
            int pos;
            string referLink = "http://www.kaixin001.com/app/app.php?aid=1040&url=match.php&matchuid=" + matchuid + "&matchid=" + matchid;
            string matchLink = "http://www.kaixin001.com/!parking/match.php?matchuid=" + matchuid + "&matchid=" + matchid;
            string content = _hh.Get(matchLink);
            string verifyCode = ContentHelper.GetVerifyCode(content);
            if (String.IsNullOrEmpty(verifyCode))
            {
                TraceLog.PrintLn("Can not get Verifycode");
                return false;
            }

            // 左边部分加油
            string teamuid = ContentHelper.GetMidString(content, "javascript:oil(", ",", out pos);
            if ((null == teamuid) || (pos < 0))
            {
                TraceLog.PrintLn("Unexpected sign - javascript:oil, return!");
                return false;
            }
            if (Int32.Parse(teamuid) == this.myID)
            {
                TraceLog.PrintLn("Skip MY Match!");
                return false;
            }
            string testContent = content.Substring(0, pos);
            content = content.Substring(pos);
            string teamid = ContentHelper.GetMidString(content, ",", " , 1)", out pos);
            int posCS = testContent.IndexOf("车速：");

            if (posCS < 0)
            {
                //已经行驶过的车，不加油
                TraceLog.PrintLn("Skip already RUNED!");
            }
            else
            {
                //为未行驶和正在行驶的车加油
                //跳过非好友
                if (this.fList.Contains(Int32.Parse(teamuid)))
                {
                    TraceLog.PrintLn("加油 TeamUID=" + teamuid + ", TeamID=" + teamid);
                    string oilLink = "http://www.kaixin001.com/parking/teamoil.php?verify=" + verifyCode + "&teamuid=" + teamuid + "&teamid=" + teamid + "&type=1&matchuid=" + matchuid + "&matchid=" + matchid;
                    //string oliResult = _hh.GetRdr(oilLink, referLink, "http://www.kaixin001.com/parking/teamoil.php");
                    string oliResult = _hh.Get(oilLink);
                    if (oliResult != null) ShowOilResult(oliResult);
                }
                else
                {
                    TraceLog.PrintLn("Skip NONE friend!");
                }
            }

            // 跳过左边部分捣鬼
            content = content.Substring(pos);
            ContentHelper.GetMidString(content, "javascript:oil(", ")", out pos);
            if (pos < 0)
            {
                TraceLog.PrintLn("Unexpected sign - javascript:oil, return!");
                return false;
            }

            // 右边部分加油
            content = content.Substring(pos);
            teamuid = ContentHelper.GetMidString(content, "javascript:oil(", ",", out pos);
            if ((null == teamuid) || (pos < 0))
            {
                TraceLog.PrintLn("Unexpected sign - javascript:oil, return!");
                return false;
            }
            if (Int32.Parse(teamuid) == this.myID)
            {
                TraceLog.PrintLn("Skip MY Match!");
                return false;
            }
            testContent = content.Substring(0, pos);
            content = content.Substring(pos);
            teamid = ContentHelper.GetMidString(content, ",", " , 1)", out pos);
            posCS = testContent.IndexOf("车速：");

            if (posCS < 0)
            {
                //已经行驶过的车，不加油
                TraceLog.PrintLn("Skip already RUNED!");
            }
            else
            {
                //跳过非好友
                if (this.fList.Contains(Int32.Parse(teamuid)))
                {
                    TraceLog.PrintLn("加油 TeamUID=" + teamuid + ", TeamID=" + teamid);
                    string oilLink = "http://www.kaixin001.com/parking/teamoil.php?verify=" + verifyCode + "&teamuid=" + teamuid + "&teamid=" + teamid + "&type=1&matchuid=" + matchuid + "&matchid=" + matchid;
                    //string oliResult = _hh.GetRdr(oilLink, referLink, "http://www.kaixin001.com/parking/teamoil.php");
                    string oliResult = _hh.Get(oilLink);
                    if (oliResult != null) ShowOilResult(oliResult);
                }
                else
                {
                    TraceLog.PrintLn("Skip NONE friend!");
                }
            }

            TraceLog.PrintLn("");
            return true;
        }

        private void ShowOilResult(string content)
        {
            content = ContentHelper.GetMidString(content, "display:block", "</strong>");
            if (content != null)
            {
                TraceLog.PrintLn(ContentHelper.NoHtml(content));
            }
            else 
            {
                TraceLog.PrintLn("None printable content");
            }
        }

        // 登录账号
        private bool Logon(string username, string password)
        {
            return LogonAndGetId(username, password) > 0;
        }

        // 登录账号 并获取用户 ID
        private int LogonAndGetId(string username, string password)
        {
            TraceLog.Print("Login as [{0}]: ", username);
            string content = _hh.Post("http://www.kaixin001.com/login/login.php", "url=%2F&email=" + HttpHelper.EncodePostData(username) + "&password=" + HttpHelper.EncodePostData(password));

            if (content.IndexOf("<title>我的首页 - 开心网</title>") != -1)
            {
                int pos;
                string strId = ContentHelper.GetMidString(content, "我的开心网ID:", "\"", out pos);
                int id = -1;
                if (strId != null)
                {
                    try
                    {
                        id = Int32.Parse(strId);
                    }
                    catch (OverflowException)
                    {
                    }
                    catch (FormatException)
                    {
                    }
                }
                TraceLog.PrintLn("Ok!, Id={0}", id);
                this.myID = id;
                return id;
            }
            else
            {
                TraceLog.Error("Failed!");
                return -1;
            }
        }

        private void ReadGlobalConfig()
        {
            try
            {
                string dir = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
                string filename = dir + "\\" + c_ConfigFile;

                if (!File.Exists(filename))
                {
                    TraceLog.PrintLn("缺少{0}文件，系统加载默认配置.", filename);
                    return;
                }

                GlobalConfigData config = GlobalConfigHelper.ReadGlobalConfigData(filename);
                _hh.SetConfig(config);
                TraceLog.WriteToFile = config.WriteToLogFile;
            }
            catch (Exception e)
            {
                TraceLog.Error(e.Message);
            }
        }

        #region Member Fields
        private const string c_ConfigFile = "Config.txt";
        private const string c_MasterIDsFile = "MasterIDs.txt";
        private const string c_SlaveIDsFile = "SlaveIDs.txt";

        private int myID = -1;

        private RedirectHttpHelper _hh = new RedirectHttpHelper();
        private Collection<int> fList;
        #endregion
    }

    class RedirectHttpHelper : HttpHelper
    {
        public string GetRdr(string url, string referer, string baseLink)
        {
            if ((url == null) || (referer == null) || (baseLink == null)) return null;
            int failedTimes = _tryTimes;
            while (failedTimes-- > 0)
            {
                try
                {
                    DelaySomeTime();

                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    req.UserAgent = reqUserAgent;
                    req.CookieContainer = _cc;
                    req.Referer = referer;

                    req.Method = "GET";
                    req.Timeout = _timeout;
                    if (null != _proxy && null != _proxy.Credentials)
                    {
                        req.UseDefaultCredentials = true;
                    }
                    req.Proxy = _proxy;
                    //req.Connection = "Keep-Alive";
                    this.setReqProps( req );

                    //Do not allow auto redirect
                    req.AllowAutoRedirect = false;

                    //handle redirect links
                    HttpWebResponse res = (HttpWebResponse)req.GetResponse();
                    baseLink += res.Headers["Location"];

                    DelaySomeTime();

                    HttpWebRequest req1 = (HttpWebRequest)WebRequest.Create(new Uri(baseLink));
                    req1.UserAgent = reqUserAgent;
                    req1.CookieContainer = _cc;
                    req1.Referer = referer;

                    req1.Method = "GET";
                    req1.Timeout = _timeout;
                    if (null != _proxy && null != _proxy.Credentials)
                    {
                        req1.UseDefaultCredentials = true;
                    }
                    req1.Proxy = _proxy;
                    //req.Connection = "Keep-Alive";
                    this.setReqProps(req1);

                    return this.getResp(req1);
                }
                catch (Exception e)
                {
                    TraceLog.Error("HTTP GET Error: " + e.Message);
                    TraceLog.Error("Url: " + url);
                }
            }

            return string.Empty;
        }
    }
}
